/*
 * multithread.c
 *
 *  Created on: Feb 28, 2010
 *      Author: vtellier
 */

#include "multithread.h"	// L'entete de ce fichier.

void start_multithread(t_bruteforce to_crack, void * function)
{
	pthread_t *		th;
	control_args *	args;
	char			temp;

	// Allocation de l'espace mémoire requis pour notre thread
	th = (pthread_t *) malloc(sizeof(pthread_t));

	// Initialisation de l'argument à passer au thread de contrôle
	args = (control_args *) malloc(sizeof(control_args));
	(*args).function	= function;
	(*args).to_crack	= to_crack;

	// On demande le nombre de threads à créer
	printf("Sur combien de threads faut-il rechercher ? ");
	scanf("%d", &((*args).nb_threads));

	pthread_create(th, NULL, threads_control, (void *) args);
//	threads_control((void *) args);
//	printf("Recherche en cours, Appuyer sur une touche pour l'annuler.\n");
//	scanf("%c",&temp);

	free(th);
}

void * threads_control(void * args)
{
	int					nb_threads;	// Le nombre de threads que l'on va créer
	pthread_t *			threads;	// Le tableau de tous les threads.
	control_args *		infos;		// Le tableau des arguments des threads.
	char *				val_init;	// La valeur de départ du thread précédent dans l'itération ci-dessous
	int *				ch_ind_init;// Tableau d'incides du charset de départ.
	int					init_len;	// La taille de la précédente chaine de caractères
	int 				is_ended;	// Le booléen de fin de recherche des threads.
	int					i, j;
	char **				result;

	nb_threads = (*((control_args *) args)).nb_threads;

	// Ce nombre correspondra au nombre de combinaisons à sauter par thread.
	(*((control_args *) args)).to_crack.jump = nb_threads;

	// Allocation de la mémoire nécessaire pour stoquer nos threads
	threads = (pthread_t *) malloc(nb_threads * sizeof(pthread_t));

	// Allocation de la mémoire nécessaire pour stoquer les paramètre de chacun des threads.
	infos	= (control_args *) malloc(nb_threads * sizeof(control_args));

	// initialisation de la première valeur de départ
	val_init	= (*((control_args *) args)).to_crack.value_at_start;
	ch_ind_init	= (*((control_args *) args)).to_crack.ch_index;
	init_len	= strlen(val_init);

	is_ended = FALSE;

	// Initialisation du tableau d'indices du charset de la combinaison de départ
	i = 0;
	while(val_init[i] != '\0')
	{
		// Différence des adresses donne l'indice.
		ch_ind_init[i]	= (*((control_args *) args)).to_crack.charset - strchr((*((control_args *) args)).to_crack.charset, val_init[i]);

		// Passage au caractère suivant
		i++;
	}

	// Initialisation de l'adresse du resultat à NULL.
	result = (char **) malloc(sizeof(char *));
	*result = NULL;

	// Création des threads
	for(i = 0; i < nb_threads; i++)
	{
	//	printf("Valeur initiale du thread n°%d : %s\n",i,val_init);
		// Les valeurs communes à tous les threads
		infos[i] = * ((control_args *) args);
		infos[i].to_crack.competitors	= threads;
		infos[i].to_crack.is_ended		= &is_ended;
		infos[i].to_crack.result		= result;

		// Initialisation du verrou
		pthread_mutex_init(&(infos[i].to_crack.mutal_ex), NULL);
		// et fermeture de celui-ci
		pthread_mutex_lock(&(infos[i].to_crack.mutal_ex));

		// La valeur de départ spécifique à ce thread, donc avec allocation
		infos[i].to_crack.value_at_start = (char *) malloc(init_len * sizeof(char));

		// Copie des valeurs
		strcpy(infos[i].to_crack.value_at_start, val_init);

		infos[i].to_crack.ch_index = (int *) malloc(init_len * sizeof(char));

		// Copie des valeurs du tableau d'indices.
		for(j=0; j<init_len; j++)
		{
			infos[i].to_crack.ch_index[j] = ch_ind_init[j];
		}
		// Création du thread
		pthread_create(threads+i, NULL, infos[i].function, (void *) &(infos[i].to_crack));

		// On passe à la valeur d'initialisation suivante pour le prochain thread
		nextStr(val_init, &init_len, 0, infos[i].to_crack.max_length, 1, infos[i].to_crack.charset, strlen(infos[i].to_crack.charset),ch_ind_init);
	}

	threads[nb_threads] = 0;

	// Démarrage de la recherche en débloquant le verrou
	for(i = 0; i < nb_threads; i++)
	{
		printf("Envoi du feu vert n°%d\n",i+1);
		pthread_mutex_unlock(&(infos[i].to_crack.mutal_ex));
	}

	printf("Attente de la fin des threads\n");

	// Attente de la fin des threads
	for (i = 0; i < nb_threads; i++)
	{
		printf("Thread %d : attente de la fin de son exécution.\n",i);
		pthread_join(threads[i], NULL);
	}

	printf("Recherche terminée.\n");

	// Destruction des verrous
	for(i = 0; i < nb_threads; i++)
	{
		pthread_mutex_destroy(&(infos[i].to_crack.mutal_ex));
	}

	if(*result != NULL)
		printf("Resultat final : %s\n\n",*result);
	else
		printf("RIEN TROUVE.\n");

//	printf("Resultat : %s\n",*result);

	return NULL;
}
